/**
 * Copyright 2009-2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.model.elem;

import java.util.ArrayList;
import java.util.List;

import chunmap.model.algorithm.EnvelopeAlgorithm;
import chunmap.model.coord.Coordinate2D;
import chunmap.model.coord.Position;
import chunmap.model.coord.Transform;
import chunmap.model.geom.Geometry;
import chunmap.model.geom.Ring;
import chunmap.model.geom.Polygon;

/**
 * 边框
 * 
 * @author chunquedong
 * 
 */
public class Envelope {

	protected double minX;
	protected double maxX;
	protected double minY;
	protected double maxY;

	public Envelope(double x1, double y1, double x2, double y2) {
		if (x1 < x2) {
			minX = x1;
			maxX = x2;
		} else {
			minX = x2;
			maxX = x1;
		}
		if (y1 < y2) {
			minY = y1;
			maxY = y2;
		} else {
			minY = y2;
			maxY = y1;
		}
	}

	public Envelope(Position point1, Position point2) {
		this(point1.getX(), point1.getY(), point2.getX(), point2.getY());
	}

	public Envelope() {
		this.minX = Double.POSITIVE_INFINITY;
		this.minY = Double.POSITIVE_INFINITY;
		this.maxX = Double.NEGATIVE_INFINITY;
		this.maxY = Double.NEGATIVE_INFINITY;
	}

	public boolean isNone() {
		return minX > maxX;
	}

	// ------------------------------------------------------------

	public double getMaxX() {
		return maxX;
	}

	public double getMinX() {
		return minX;
	}

	public double getMaxY() {
		return maxY;
	}

	public double getMinY() {
		return minY;
	}

	public Position getMinPoint() {
		return new Coordinate2D(minX, minY);
	}

	public Position getMaxPoint() {
		return new Coordinate2D(maxX, maxY);
	}
	
	public Position leftUp()
    {
        return new Coordinate2D(minX, maxY);
    }
    public Position rightDown()
    {
        return new Coordinate2D(maxX, minY);
    }

	public double getHeight() {
		return maxY - minY;
	}

	public double getWidth() {
		return maxX - minX;
	}

	// ------------------------------------------------------------

	/**
	 * 得到中心点
	 * 
	 * @return
	 */
	public Position getCenter() {
		double x = (minX + maxX) / 2d;
		double y = (minY + maxY) / 2d;
		return new Coordinate2D(x, y);
	}

	public Envelope transform(Transform transf) {
		Position p1 = transf.convert(getMinPoint());
		Position p2 = transf.convert(getMaxPoint());
		return new Envelope(p1, p2);
	}

	/**
	 * 边框内是否包含指定点,在边上时返回true
	 * 
	 * @param point
	 * @return
	 */
	public boolean contain(Position point) {
		double x = point.getX();
		double y = point.getY();

		if (x < minX || x > maxX)
			return false;
		if (y < minY || y > maxY)
			return false;
		return true;
	}

	public boolean contain(Envelope env) {

		if (!this.contain(env.getMinPoint()))
			return false;
		if (!this.contain(env.getMaxPoint()))
			return false;
		return true;
	}

	/**
	 * 与指定边框是否有交集
	 * 
	 * @param envelop
	 * @return
	 */
	public boolean hasIntersect(Envelope envelop) {
		if (envelop.getMinY() > maxY)
			return false;
		else if (envelop.getMaxY() < minY)
			return false;
		else if (envelop.getMinX() > maxX)
			return false;
		else if (envelop.getMaxX() < minX)
			return false;
		else
			return true;
	}
	
	public Envelope intersection(Envelope envelop) {
		double minx=Math.max(minX, envelop.getMinX());
		double miny=Math.max(minY, envelop.getMinY());
		double maxx=Math.min(maxX, envelop.getMaxX());
		double maxy=Math.min(maxY, envelop.getMaxY());
		return new Envelope(minx,miny,maxx,maxy);
	}

	public boolean hasIntersect(Geometry geom) {
		if (!this.hasIntersect(geom.getEnvelop())) return false;

        return EnvelopeAlgorithm.hasIntersect(this, geom);
	}

	@Override
	public String toString() {
		return "Envelop [maxX=" + maxX + ", maxY=" + maxY + ", minX=" + minX
				+ ", minY=" + minY + "]";
	}

	public Polygon toPolygon() {
		Polygon pg = new Polygon(toRing());
		pg._setValid(true);
		return pg;
	}
	
	public Ring toRing() {
		List<Position> points = new ArrayList<Position>();
		Position p1 = new Coordinate2D(minX, minY);
		Position p2 = new Coordinate2D(maxX, minY);
		Position p3 = new Coordinate2D(maxX, maxY);
		Position p4 = new Coordinate2D(minX, maxY);
		points.add(p1);
		points.add(p2);
		points.add(p3);
		points.add(p4);
		points.add(p1);

		return new Ring(points);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits(maxX);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(maxY);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(minX);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(minY);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Envelope other = (Envelope) obj;
		if (Double.doubleToLongBits(maxX) != Double
				.doubleToLongBits(other.maxX))
			return false;
		if (Double.doubleToLongBits(maxY) != Double
				.doubleToLongBits(other.maxY))
			return false;
		if (Double.doubleToLongBits(minX) != Double
				.doubleToLongBits(other.minX))
			return false;
		if (Double.doubleToLongBits(minY) != Double
				.doubleToLongBits(other.minY))
			return false;
		return true;
	}
}
